package com.tree.backend.utils;

import org.springframework.beans.BeanUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.Collections;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * 通用数据映射工具类
 * 用于处理实体间的外键关联数据映射和填充
 * 支持多外键的批量查询和映射填充，避免N+1查询问题
 */
public class DataMappingUtils {
    
    /**
     * 特殊标记键，用于标识已经过权限过滤但没有数据的情况
     */
    public static final String PERMISSION_FILTERED_FLAG = "__PERMISSION_FILTERED__";

    /**
     * 批量查询外键关联数据并构建ID到名称的映射
     * 
     * @param records 分页实体列表
     * @param idExtractor 从实体中提取外键ID的函数
     * @param dataProvider 根据ID列表查询数据的函数
     * @param idMapper 从关联数据中提取ID的函数
     * @param nameMapper 从关联数据中提取名称的函数
     * @param existingMap 已存在的映射，如果为null则创建新的
     * @param <T> 实体类型
     * @param <K> ID类型
     * @param <D> 关联数据类型
     * @return 填充后的ID到名称的映射
     */
    public static <T, K, D> Map<K, String> buildIdNameMap(
            List<T> records,
            Function<T, K> idExtractor,
            Function<Collection<K>, List<D>> dataProvider,
            Function<D, K> idMapper,
            Function<D, String> nameMapper,
            Map<K, String> existingMap) {
        
        Map<K, String> resultMap = existingMap != null ? existingMap : new HashMap<>();
        
        // 检查是否有特殊标记，表示已经过权限过滤但没有数据
        if (resultMap.containsKey((K) PERMISSION_FILTERED_FLAG)) {
            // 如果只有特殊标记，返回空映射（不包含标记）
            if (resultMap.size() == 1) {
                return new HashMap<>();
            }
            // 否则创建一个不包含特殊标记的新映射
            Map<K, String> filteredMap = new HashMap<>();
            for (Map.Entry<K, String> entry : resultMap.entrySet()) {
                if (!PERMISSION_FILTERED_FLAG.equals(entry.getKey())) {
                    filteredMap.put(entry.getKey(), entry.getValue());
                }
            }
            return filteredMap;
        }
        
        // 如果records为空，直接返回现有映射
        if (records == null || records.isEmpty()) {
            return resultMap;
        }
        
        // 如果现有映射已经包含所有需要的ID，直接返回
        if (!resultMap.isEmpty()) {
            Set<K> requiredIds = records.stream()
                    .map(idExtractor)
                    .filter(Objects::nonNull)
                    .filter(id -> !resultMap.containsKey(id))
                    .collect(Collectors.toSet());
            
            if (requiredIds.isEmpty()) {
                return resultMap;
            }
        } else {
            // 获取所有需要查询的非空ID
            Set<K> requiredIds = records.stream()
                    .map(idExtractor)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toSet());
            
            // 查询关联数据并构建映射
            if (!requiredIds.isEmpty()) {
                List<D> relatedData = dataProvider.apply(requiredIds);
                if (relatedData != null && !relatedData.isEmpty()) {
                    Map<K, String> tempMap = relatedData.stream()
                            .collect(Collectors.toMap(
                                    idMapper,
                                    nameMapper,
                                    (existing, replacement) -> existing // 处理冲突，保留现有值
                            ));
                    resultMap.putAll(tempMap);
                }
            }
        }
        
        return resultMap;
    }
    
    /**
     * 将实体列表转换为VO列表，并填充外键关联的名称
     * 
     * @param records 分页实体列表
     * @param voClass VO类类型
     * @param idExtractor 从实体中提取外键ID的函数
     * @param nameMap ID到名称的映射
     * @param nameSetter 设置VO中名称字段的函数
     * @param <T> 实体类型
     * @param <VO> VO类型
     * @param <K> ID类型
     * @return 转换并填充后的VO列表
     */
    public static <T, VO, K> List<VO> convertAndFillNames(
            List<T> records,
            Class<VO> voClass,
            Function<T, K> idExtractor,
            Map<K, String> nameMap,
            BiConsumer<VO, String> nameSetter) {
        return convertAndFillNames(records, voClass, idExtractor, nameMap, nameSetter, null);
    }
    
    /**
     * 将实体列表转换为VO列表，并填充外键关联的名称（支持已存在的VO列表）
     * 
     * @param records 分页实体列表
     * @param voClass VO类类型
     * @param idExtractor 从实体中提取外键ID的函数
     * @param nameMap ID到名称的映射
     * @param nameSetter 设置VO中名称字段的函数
     * @param existingVOs 已存在的VO列表，如果为null则创建新的
     * @param <T> 实体类型
     * @param <VO> VO类型
     * @param <K> ID类型
     * @return 转换并填充后的VO列表
     */
    public static <T, VO, K> List<VO> convertAndFillNames(
            List<T> records,
            Class<VO> voClass,
            Function<T, K> idExtractor,
            Map<K, String> nameMap,
            BiConsumer<VO, String> nameSetter,
            List<VO> existingVOs) {
        
        // 检查名称映射是否包含特殊标记，表示已经过权限过滤但没有数据
        if (nameMap != null && nameMap.containsKey((K) PERMISSION_FILTERED_FLAG) && nameMap.size() == 1) {
            // 如果只有特殊标记，直接返回空列表，不创建任何VO对象
            return Collections.emptyList();
        }
        
        if (records == null || records.isEmpty()) {
            return existingVOs != null ? existingVOs : Collections.emptyList();
        }
        
        // 如果提供了existingVOs，需要确保它与records大小一致
        if (existingVOs != null && existingVOs.size() != records.size()) {
            throw new IllegalArgumentException("Size of existingVOs must match size of records");
        }
        
        return IntStream.range(0, records.size()).mapToObj(index -> {
            T record = records.get(index);
            VO vo;
            try {
                if (existingVOs != null && index < existingVOs.size()) {
                    // 使用已存在的VO对象
                    vo = existingVOs.get(index);
                } else {
                    // 创建新的VO对象
                    vo = voClass.getDeclaredConstructor().newInstance();
                    BeanUtils.copyProperties(record, vo);
                }
                
                // 填充名称
                K id = idExtractor.apply(record);
                String name = nameMap != null ? nameMap.getOrDefault(id, "") : "";
                nameSetter.accept(vo, name);
                
                return vo;
            } catch (Exception e) {
                throw new RuntimeException("Failed to create and fill " + voClass.getSimpleName(), e);
            }
        }).collect(Collectors.toList());
    }
    
    /**
     * 简化版：直接填充数据并返回新的Map（如果提供的Map为空）
     * 
     * @param records 分页实体列表
     * @param idExtractor 从实体中提取外键ID的函数
     * @param dataProvider 根据ID列表查询数据的函数
     * @param idMapper 从关联数据中提取ID的函数
     * @param nameMapper 从关联数据中提取名称的函数
     * @param <T> 实体类型
     * @param <K> ID类型
     * @param <D> 关联数据类型
     * @return 新的或更新后的ID到名称的映射
     */
    public static <T, K, D> Map<K, String> fillMapIfEmpty(
            List<T> records,
            Function<T, K> idExtractor,
            Function<Collection<K>, List<D>> dataProvider,
            Function<D, K> idMapper,
            Function<D, String> nameMapper) {
        return buildIdNameMap(records, idExtractor, dataProvider, idMapper, nameMapper, new HashMap<>());
    }

    /**
     * 通用权限过滤方法，根据条件查询相关实体并添加过滤条件
     * 适用于任何需要基于权限实体进行数据过滤的场景
     * 
     * @param permissionCondition 权限过滤条件判断函数
     * @param permissionDataProvider 根据权限条件获取权限实体列表的函数
     * @param permissionIdExtractor 从权限实体中提取ID的函数
     * @param permissionNameExtractor 从权限实体中提取名称的函数
     * @param permissionMap 权限ID到名称的映射Map
     * @param queryWrapper 查询条件构建器
     * @param entityPermissionFieldExtractor 从目标实体中提取权限字段的函数
     * @param <P> 权限实体类型
     * @param <T> 目标实体类型
     * @param <K> 权限ID类型
     * @return 是否应用了权限过滤
     */
    public static <P, T, K> boolean applyPermissionFilter(
            Supplier<Boolean> permissionCondition,
            Supplier<List<P>> permissionDataProvider,
            Function<P, K> permissionIdExtractor,
            Function<P, String> permissionNameExtractor,
            Map<K, String> permissionMap,
            LambdaQueryWrapper<T> queryWrapper,
            SFunction<T, K> entityPermissionFieldExtractor) {
        
        // 检查权限条件是否满足
        if (permissionCondition == null || !permissionCondition.get()) {
            return false;
        }
        
        // 获取权限数据
        List<P> permissionEntities = permissionDataProvider.get();
        if (permissionEntities == null || permissionEntities.isEmpty()) {
            // 如果没有权限数据，但需要应用权限过滤，添加特殊标记
            if (permissionMap != null) {
                permissionMap.put((K) PERMISSION_FILTERED_FLAG, "");
            }
            // 使用永远不会匹配的条件，避免生成空的IN子句导致SQL语法错误
            queryWrapper.eq(entityPermissionFieldExtractor, "");
            return true;
        }
        
        // 构建权限ID到名称的映射
        Map<K, String> tempMap = permissionEntities.stream()
                .collect(Collectors.toMap(
                        permissionIdExtractor,
                        permissionNameExtractor,
                        (existing, replacement) -> existing // 处理重复键，保留第一个值
                ));
        
        // 添加到传入的映射中
        if (permissionMap != null) {
            permissionMap.putAll(tempMap);
        }
        
        // 添加权限过滤条件
        queryWrapper.in(entityPermissionFieldExtractor, tempMap.keySet());
        return true;
    }
    
    /**
     * 通用分页转换方法，将Page<T>转换为Page<VO>
     * 
     * @param sourcePage 源Page对象
     * @param voClass VO类类型
     * @param <T> 源数据类型
     * @param <VO> 目标VO类型
     * @return 转换后的Page<VO>
     */
    public static <T, VO> Page<VO> convertPage(Page<T> sourcePage, Class<VO> voClass) {
        if (sourcePage == null) {
            return new Page<>();
        }
        
        // 转换records列表
        List<VO> voList = convertList(sourcePage.getRecords(), voClass);
        
        // 创建新的Page对象并复制属性
        Page<VO> targetPage = new Page<>();
        BeanUtils.copyProperties(sourcePage, targetPage);
        targetPage.setRecords(voList);
        
        return targetPage;
    }
    
    /**
     * 通用列表转换方法，将List<T>转换为List<VO>
     * 
     * @param sourceList 源列表
     * @param voClass VO类类型
     * @param <T> 源数据类型
     * @param <VO> 目标VO类型
     * @return 转换后的List<VO>
     */
    public static <T, VO> List<VO> convertList(List<T> sourceList, Class<VO> voClass) {
        if (sourceList == null || sourceList.isEmpty()) {
            return Collections.emptyList();
        }
        
        return sourceList.stream().map(item -> {
            try {
                VO vo = voClass.getDeclaredConstructor().newInstance();
                BeanUtils.copyProperties(item, vo);
                return vo;
            } catch (Exception e) {
                throw new RuntimeException("Failed to convert " + item.getClass().getSimpleName() + " to " + voClass.getSimpleName(), e);
            }
        }).collect(Collectors.toList());
    }
    
    /**
     * 函数式接口，用于接受两个参数并执行操作，类似于BiConsumer
     */
    @FunctionalInterface
    public interface BiConsumer<T, U> {
        void accept(T t, U u);
    }
}